interface Option {
  leading?: boolean;
  maxDelay?: number;
  trailing?: boolean;
}

/**
 * 基础方法
 * @param func 执行函数
 * @param delay 延迟（毫秒）
 * @param option 可选配置信息
 * @param option.leading 首端是否执行
 * @param option.maxDelay 最长延迟（毫秒）
 * @param option.trailing 尾端是否执行
 */
const base = function (func: Function, delay: number, option: Option) {
  let { leading, maxDelay = Number.MAX_VALUE, trailing } = option;
  let cancelled = false,
    timerId: number = 0,
    currentArgs: any = undefined,
    currentThis: any = undefined,
    lastCallTime = 0,
    lastExecTime = 0;

  maxDelay = maxDelay < delay ? delay : maxDelay;

  function doFunc(now: number) {
    const args = currentArgs,
      self = currentThis;

    lastExecTime = now;
    func.apply(self, args);
  }

  function doTrailing(now: number) {
    if (trailing && (lastExecTime == 0 || lastCallTime < lastExecTime)) {
      doFunc(now);
    }
    timerId = 0;
    currentArgs = currentThis = undefined;
    lastCallTime = lastExecTime = 0;
  }

  // 调用时间超过延时就停止生长
  function grow() {
    const now = Date.now();
    const diffCallTime = now - lastCallTime;
    const diffExecTime = now - lastExecTime;

    // 调用间隔超过延时，就表示本轮生长进入结束环节
    if (diffCallTime >= delay) {
      doTrailing(now);
    } else {
      // 如果执行间隔超过最大延时，就执行
      if (diffExecTime >= maxDelay) {
        doFunc(now);
      }

      // 计算下次最近的执行时间
      const remainingTime = Math.min(maxDelay - diffExecTime, delay - diffCallTime);
      timerId = window.setTimeout(grow, remainingTime);
    }
  }

  // 构造竹子，执行了就类似生成竹节
  function bamboo(now: number) {
    timerId = window.setTimeout(grow, delay);

    if (lastCallTime == 0 && leading) {
      doFunc(now);
    }
  }

  // 取消
  function cancel() {
    cancelled = true;
  }

  // 外壳
  function shell() {
    if (cancelled) {
      return;
    }
    (function () {
      const now = Date.now();
      // @ts-ignore
      currentThis = this;
      currentArgs = arguments;

      if (timerId == 0) {
        bamboo(now);
      }

      lastCallTime = now;
    })();
  }

  shell.cancel = cancel;

  return shell;
};

/**
 * 防抖
 * @param func 执行函数
 * @param delay 延迟毫秒
 * @param option 参考base配置
 * @returns
 */
export const debounce = function (func: Function, delay: number, option: Option = {}) {
  const { leading = false, maxDelay = Number.MAX_VALUE, trailing = true } = option;

  return base(func, delay, { leading, maxDelay, trailing });
};

/**
 * 节流
 * @param func 执行函数
 * @param delay 延迟毫秒
 * @param option 参考base配置
 * @returns
 */
export const throttle = function (func: Function, delay: number, option: Option = {}) {
  const { leading = true, maxDelay = delay, trailing = true } = option;

  return base(func, delay, { leading, maxDelay, trailing });
};
